""" 网络编程 """

import socket
import ssl
import re


def create_socket(en_ssl, hostname):
    """根据 en_ssl 创建 socket

    Returns:
        [socket.socket]: socket 对象
    """
    if en_ssl:
        context = ssl.create_default_context()
        sock = socket.create_connection((hostname, 443))
        return context.wrap_socket(sock, server_hostname=hostname)
    return socket.socket(socket.AF_INET, socket.SOCK_STREAM)


def get_url(url, request_headers=None):
    """socket模拟HTTP请求

    Args:
        url (str): 需要请求的地址
        request_headers (list): 自定义请求头.

    Returns:
        dict: { code, message, headers, response }  { 状态码, 状态信息, 请求头, Response }
    """
    # 参数处理
    pattern = r'''
        (http|https)?                                                           # 非贪婪匹配 'http|https'
        (://)?                                                                  # 非贪婪匹配 '://'
        ([a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+\.?)    # 匹配域名
        (.*)                                                                    # 匹配域名之后的 path 和 search
    ''' # 松散表达式
    matches = re.finditer(pattern, url, re.VERBOSE)
    parse_url = [(match.group(1), match.group(3), match.group(5)) for match in matches]
    http, host, path = parse_url[0]
    path = path if path else '/'
    en_ssl = False if http is None or http == 'http' else True

    # 建立 socket
    with create_socket(en_ssl, hostname= host) as _s:
        # 建立连接:
        if not en_ssl:
            _s.connect((host, 80))
        # 发送数据:
        _s.send(F'GET {path} HTTP/1.1\r\n'.encode('utf-8'))
        _s.send(F'Host: {host}\r\n'.encode('utf-8'))
        # 自定义请求头
        if request_headers is not None:
            for _v in request_headers:
                _s.send(_v)
        _s.send(b'Connection: close\r\n\r\n')
        # 接收数据:
        buffer = []
        while True:
            # 每次最多接收 8k 字节:
            _d = _s.recv(8192)
            print('正在获取数据, 请稍等...')
            if _d:
                buffer.append(_d)
            else:
                break
        # 数据处理
        data = b''.join(buffer)
        _header, _response = data.split(b'\r\n\r\n', 1)
        _header = _header.decode('utf-8')
        status_pattern = r'(HTTP/1.1 (\d*) (\w*)\s*)'
        status_arr = re.findall(status_pattern, _header)
        # 'ignore' solve
        # 'utf-8' codec can't decode byte 0xe5 in position 129821: invalid continuation byte
        return {
            'code': status_arr[0][1],
            'message': status_arr[0][2],
            'headers': _header.replace(status_arr[0][0], '', 1),
            'response': _response.decode('utf-8', 'ignore')
        }


if __name__ == '__main__':
    try:
        b_res = get_url('http://www.baidu.com')
        s_res = get_url(
            'https://www.sogou.com/web?query=ip',
            request_headers=[
                'User-Agent: Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 \
                (KHTML, like Gecko) Chrome/96.0.4664.110 Safari/537.36\r\n'.encode('utf-8')
            ]
        )
        IP_PATTERN = r'SgConst.common.clientIp = "(.*)";'
        ip_arr = re.findall(IP_PATTERN, s_res['response'])
        print('状态码=', b_res['code'])
        print('请求头=', b_res['headers'])
        print('您的IP的地址=', ip_arr[0])
    except (re.error, UnicodeDecodeError, ValueError) as e:
        print(e)
